from django.db.models import Q
from django.utils import timezone
from utils.mixins import PartUpdateModelMixin
from drf_spectacular.utils import extend_schema
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.mixins import (CreateModelMixin, ListModelMixin,
                                   RetrieveModelMixin, UpdateModelMixin)
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from utils.autoschema import AutoDocSchema
from utils.decorators import error_response, request_data, activities_logging

import backend.cews.services.PartsService as PartsService
from backend.cews.models import Parts, Log
from backend.cews.services.RepairService import get_maintenance_history

from .serializers import (AssetSerializer, EquipmentLevelRelationSerializer,
                          PartsHardTimeReplacementSerializer, PartsSerializer,
                          PartVersionSerializer,
                          RepairOrderMaintenanceHistorySerializer,
                          RepairOrderPartReplacementSerializer,
                          SystemSerializer, WorkgroupSerializer, PartsWithChildSerializer)


class PartsViewSet(
    RetrieveModelMixin,
    ListModelMixin,
    PartUpdateModelMixin,
    GenericViewSet,
    CreateModelMixin
):
    serializer_class = PartsSerializer
    queryset = Parts.objects.filter(deleted=False)

    @error_response()
    def update(self, request, *args, **kwargs):
        """
            Revision:
                - No need to update for now 2023 06 23 Cass
        """
        # PartsService.update_date_code(
        #     request.data["stock_code"],
        #     request.data["serial_no"],
        #     request.data["date_code"]
        # )

        return super().update(request, *args, **kwargs)

    @action(
        detail=False, methods=["POST"],
        url_name="create_part", schema=AutoDocSchema()
    )
    @request_data(
        keys=[
            "stock_code",
            "serial_no",
            "system",
            "car",
            "workgroup",
            "short_description",
            "description",
            "date_code",
            "sw_ver",
            "hw_ver",
            "asset_id",
            "equipment_class",
            "lv1",
            "lv2",
            "lv3",
            "lv4",
            "lv5",
            "parent_part_stock_code",
            "parent_part_serial_number",
            "obsolescence",
            "stock_item_regular_cycle_month",
        ],
        types={
            "stock_code": str,
            "serial_no": str,
            "system": str,
            "short_description": str,
            "asset_id": str,
            "equipment_class": str,
            "lv1": str,
            "lv2": str,
            "lv3": str,
            "lv4": str,
            "stock_item_regular_cycle_month": int,
            "obsolescence": int,
        },
        optional=[
            "description",
            "date_code",
            "sw_ver",
            "hw_ver",
            "obsolescence",
            "lv5",
            "parent_part_stock_code",
            "parent_part_serial_number",
            "stock_item_regular_cycle_month",
        ]
    )
    @error_response()
    def create_part(
        self,
        request,
        stock_code,
        serial_no,
        system,
        car,
        workgroup,
        short_description,
        equipment_class,
        lv1,
        lv2,
        lv3,
        lv4,
        asset_id="",
        description="",
        date_code="",
        sw_ver="",
        hw_ver="",
        lv5="",
        parent_part_stock_code="",
        parent_part_serial_number="",
        obsolescence=0,
        stock_item_regular_cycle_month=36,
    ):
        '''
        desc: create new part and returns qr code
        json:
            stock_code: string
            serial_no: string
            system: string
            car: string
            workgroup: string
            short_description: string
            description:
                required: false
                type: string

            date_code:
                required: false
                type: string

            sw_ver:
                required: false
                type: string

            hw_ver:
                required: false
                type: string

            asset_id:
                type: string
                required: false

            equipment_class: string
            lv1: string
            lv2: string
            lv3: string
            lv4: string
            lv5:
                required: false
                type: string

            parent_part_stock_code:
                required: false
                type: string

            parent_part_serial_number:
                required: false
                type: string

            stock_item_regular_cycle_month:
                required: true
                type: integer

        response:
            "200":
                qr_code: object
                date: string
                time: string

        '''

        new_part, _ = PartsService.save_part(
            stock_code,
            serial_no,
            system,
            car,
            workgroup,
            short_description,
            description,
            date_code,
            sw_ver,
            hw_ver,
            equipment_class,
            lv1,
            lv2,
            lv3,
            lv4,
            lv5,
            asset_id,
            parent_part_stock_code,
            parent_part_serial_number,
            obsolescence,
            request,
            stock_item_regular_cycle_month
        )

        if new_part is not None:
            response_data = {
                "qr_code": {
                    "CAT": new_part.stock_code,
                    "SN": new_part.serial_no
                },
                "date": timezone.localtime(new_part.create).strftime("%Y/%m/%d"),
                "time": timezone.localtime(new_part.create).strftime("%#I:%M%p"),
                "id": new_part.id
            }

            return Response(status=status.HTTP_200_OK, data=response_data)

        return Response(status=status.HTTP_404_NOT_FOUND, data={
            "message": "Error on part creation"
        })

    @extend_schema(
        responses={200: PartsSerializer(many=True)},
    )
    @action(
        detail=False, methods=["POST"],
        url_name="get_part", schema=AutoDocSchema()
    )
    @request_data(
        keys=["stock_code", "serial_no"],
        types={"stock_code": str, "serial_no": str}
    )
    def get_part(self, request, stock_code, serial_no):
        '''
        desc: Get part
        json:
            stock_code: string
            serial_no: string
        '''

        part = PartsService.get_part(
            stock_code, serial_no
        )

        serializer = PartsSerializer(part, many=True)
        return Response(status=status.HTTP_200_OK, data=serializer.data)

    @extend_schema(
        responses={200: PartVersionSerializer(many=True)},
    )
    @action(
        detail=False, methods=["POST"],
        url_name="get_version", schema=AutoDocSchema()
    )
    @request_data(keys=["stock_code", "serial_number"])
    def get_verison(self, request, stock_code, serial_number):
        '''
        desc: Get verison of the part
        json:
            stock_code: string
            serial_number: string
        '''

        serializer = PartVersionSerializer(
            PartsService.get_parts_version(stock_code, serial_number),
            many=True
        )

        return Response(status=status.HTTP_200_OK, data=serializer.data)

    @extend_schema(
        responses={200: PartsSerializer(many=True)},
    )
    @request_data(
        keys=["parent_stock_code", "parent_serial_no"]
    )
    @action(
        detail=False, methods=["POST"],
        url_name="get_child_parts", schema=AutoDocSchema()
    )
    def get_child_parts(
        self,
        request,
        parent_stock_code,
        parent_serial_no
    ):
        '''
        desc: get all child parts for the parent part
        json:
            parent_stock_code: string
            parent_serial_no: string
        '''
        result_set = PartsService.get_child_parts(
            parent_stock_code,
            parent_serial_no
        )

        serializer = PartsSerializer(result_set, many=True)
        return Response(status=status.HTTP_200_OK, data=serializer.data)

    @extend_schema(
        responses={200: PartsSerializer(many=True)},
    )
    @action(
        detail=False, methods=["POST"],
        url_name="get_all_lv5_stock_code", schema=AutoDocSchema()
    )
    def get_all_lv5_stock_code(
        self,
        request
    ):
        '''
        desc: Get all lv5 stock code from part master.
        '''
        serializer = PartsSerializer(
            PartsService.get_all_lv5_stock_code(),
            many=True
        )

        return Response(status=status.HTTP_200_OK, data=serializer.data)

    @extend_schema(
        responses={200: PartsSerializer()},
    )
    @action(
        detail=False, methods=["POST"],
        url_name="assign_part_child", schema=AutoDocSchema()
    )
    @request_data(
        keys=[
            "parent_part_stock_code",
            "parent_part_serial_no",
            "stock_code",
            "serial_no",
            "sequence"
        ]
    )
    @error_response()
    def assign_part_child(
        self,
        request,
        parent_part_stock_code,
        parent_part_serial_no,
        stock_code,
        serial_no,
        sequence
    ):
        '''
        desc: Assign child part to parent.
        json:
            parent_part_stock_code: string
            parent_part_serial_no: string
            stock_code: string
            serial_no: string
            sequence: number
        '''
        response = {
            "code": 0,
            "message": "Failed to assign child."
        }

        child_part = PartsService.assign_part_child(
            stock_code,
            serial_no,
            parent_part_stock_code,
            parent_part_serial_no,
            sequence,
            request
        )

        if child_part is not None:
            serializer = PartsSerializer(child_part)
            response = serializer.data

        return Response(status=status.HTTP_200_OK, data=response)

    @action(
        detail=False, methods=["POST"],
        url_name="remove_part_child", schema=AutoDocSchema()
    )
    @request_data(
        keys=[
            "parent_part_stock_code",
            "parent_part_serial_no",
            "stock_code",
            "serial_no"
        ]
    )
    def remove_part_child(
        self,
        request,
        parent_part_stock_code,
        parent_part_serial_no,
        stock_code,
        serial_no
    ):
        '''
        desc: Remove part child from parent.
        json:
            parent_part_stock_code: string
            parent_part_serial_no: string
            stock_code: string
            serial_no: string
        responses:
            "200":
                code: number
                message: string
        '''
        response = {}

        try:
            if PartsService.remove_part_child(
                stock_code,
                serial_no,
                parent_part_stock_code,
                parent_part_serial_no,
                request
            ):
                response = {
                    "code": 1,
                    "message": "Success to remove child."
                }
            return Response(status=status.HTTP_200_OK, data=response)

        except ValueError as ex:
            return Response(status=status.HTTP_400_BAD_REQUEST, data={
                "code": 0,
                "message": str(ex)
            })

    """ @activities_logging(
        log_type=Log.LogType.PART,
        action="reorder_part_child",
        description="Part item sequence re-order"
    ) """
    @action(
        detail=False, methods=["POST"],
        url_name="reorder_part_child", schema=AutoDocSchema()
    )
    @request_data(
        keys=[
            "serial_no_sequence_json"
        ]
    )
    def reorder_part_child(
        self,
        request,
        serial_no_sequence_json
    ):
        '''
        desc: Re-order part child.
        json:
            serial_no_sequence_json: object
        responses:
            "200":
                code: number
                message: string
        '''
        response = {
            "code": 0,
            "message": "Failed to re-order child."
        }

        if PartsService.reorder_part_child(
            serial_no_sequence_json
        ):
            response = {
                "code": 1,
                "message": "Success to re-order child."
            }

        return Response(status=status.HTTP_200_OK, data=response)

    """ @activities_logging(
        log_type=Log.LogType.PART,
        action="update_check_in_status",
        description="Part item check in"
    ) """
    @request_data(
        keys=["stock_code", "serial_number"],
        types={"stock_code": str, "serial_number": str}
    )
    @action(
        detail=False, methods=["POST"],
        url_name="update_check_in_status", schema=AutoDocSchema()
    )
    def update_check_in_status(self, request, stock_code, serial_number):
        '''
        desc: update check in status and datetime of the part.
        json:
            stock_code: string
            serial_number: string
        response:
            "200"
        '''

        _ = PartsService.update_check_in_status(
            stock_code,
            serial_number
        )

        # serializer = PartsSerializer(result_set, many=True)
        return Response(status=status.HTTP_200_OK)  # , data=serializer.data)

    # @request_data(
    #     keys=["stock_code", "serial_number"],
    #     types={"stock_code": str, "serial_number": str}
    # )
    # @action(
    #     detail=False, methods=["POST"],
    #     url_name="update_check_out_status", schema=AutoDocSchema()
    # )
    # def update_check_out_status(self, request, stock_code, serial_number):
    #     '''
    #     desc: update check out status and datetime of the part.
    #     json:
    #         stock_code: string
    #         serial_number: string
    #     response:
    #         "200"
    #     '''

    #     _ = PartsService.update_check_out_status(
    #         stock_code,
    #         serial_number
    #     )

    #     # serializer = PartsSerializer(result_set, many=True)
    #     return Response(status=status.HTTP_200_OK)  # , data=serializer.data)

    # @extend_schema(
    #     responses={200: PartsSerializer(many=True)},
    # )
    # @action(
    #     detail=False, methods=["POST"], url_name="get_checked_in_parts",
    #     schema=AutoDocSchema()
    # )
    # @request_data(
    #     keys=["stock_code", "serial_no"],
    #     types={"serial_no": str, "stock_code": str}
    # )
    # def get_checked_in_parts(self, request, stock_code, serial_no):
    #     '''
    #         desc: Get checked in parts

    #         json:
    #             stock_code: string
    #             serial_no: string
    #     '''
    #     result_set = PartsService.get_check_in_part(
    #         stock_code, serial_no
    #     )

    #     serializer = PartsSerializer(result_set, many=True)
    #     return Response(status=status.HTTP_200_OK, data=serializer.data)

    @extend_schema(
        responses={
            200: RepairOrderMaintenanceHistorySerializer(many=True)
        }
    )
    @action(
        detail=False, methods=["POST"], url_name="get_maintenance_history",
        schema=AutoDocSchema()
    )
    @request_data(
        keys=["serial_numbers"],
        types={"serial_numbers": list}
    )
    def get_maintenance_history(
        self,
        request,
        serial_numbers
    ):
        '''
            desc: Get maintenance history

            json:
                serial_numbers:
                    type: array
                    example: [""]
        '''
        serializer = RepairOrderMaintenanceHistorySerializer(
            get_maintenance_history(
                serial_numbers=serial_numbers
            ),
            many=True
        )

        return Response(
            status=status.HTTP_200_OK,
            data=serializer.data
        )

    @extend_schema(
        responses={
            200: AssetSerializer(many=True)
        }
    )
    @action(
        detail=False, methods=["POST"], url_name="get_level_options",
        schema=AutoDocSchema()
    )
    def get_level_options(
        self,
        request,
    ):
        '''
            desc: Get level options
        '''
        serializer = AssetSerializer(
            PartsService.get_level_options(),
            many=True
        )

        return Response(
            status=status.HTTP_200_OK,
            data=serializer.data
        )

    @extend_schema(
        responses={
            200: EquipmentLevelRelationSerializer(many=True)
        }
    )
    @action(
        detail=False, methods=["POST"], url_name="get_equipment_level",
        schema=AutoDocSchema()
    )
    @request_data(
        keys=["equipment_class"],
        types={"equipment_class": str}
    )
    def get_equipment_level(
        self,
        request,
        equipment_class
    ):
        '''
            desc: Get equipment level
            json:
                equipment_class: string
        '''
        serializer = EquipmentLevelRelationSerializer(
            PartsService.get_equipment_level(
                equipment_class
            ),
            many=True
        )

        return Response(
            status=status.HTTP_200_OK,
            data=serializer.data
        )

    @extend_schema(
        responses={
            200: SystemSerializer(many=True)
        }
    )
    @action(
        detail=False, methods=["POST"], url_name="get_system_options",
        schema=AutoDocSchema()
    )
    def get_system_options(
        self,
        request,
    ):
        '''
            desc: Get system options
        '''
        serializer = SystemSerializer(
            PartsService.get_system_options(),
            many=True
        )

        return Response(
            status=status.HTTP_200_OK,
            data=serializer.data
        )

    @extend_schema(
        responses={
            200: WorkgroupSerializer(many=True)
        }
    )
    @action(
        detail=False, methods=["POST"], url_name="get_workgroup_options",
        schema=AutoDocSchema()
    )
    def get_workgroup_options(
        self,
        request,
    ):
        '''
            desc: Get workgroup options
        '''
        serializer = WorkgroupSerializer(
            PartsService.get_workgroup_options(),
            many=True
        )

        return Response(
            status=status.HTTP_200_OK,
            data=serializer.data
        )

    @extend_schema(
        responses={
            200: PartsHardTimeReplacementSerializer(many=True)
        }
    )
    @action(
        detail=False, methods=["POST"], url_name="get_hard_time_replacement",
        schema=AutoDocSchema()
    )
    @request_data(
        keys=["stock_code", "serial_no"]
    )
    def get_hard_time_replacement(
        self,
        request,
        stock_code,
        serial_no,
    ):
        '''
            desc: Get hard time replacement from parts.

            json:
                stock_code: string
                serial_no: string
        '''
        serializer = PartsHardTimeReplacementSerializer(
            PartsService.get_hard_time_replacement(
                stock_code,
                serial_no
            ),
            many=True
        )

        return Response(
            status=status.HTTP_200_OK,
            data=serializer.data
        )

    @extend_schema(
        responses={
            200: RepairOrderPartReplacementSerializer(many=True)
        }
    )
    @action(
        detail=False,
        methods=["POST"],
        url_name="get_replacement_task_history",
        schema=AutoDocSchema()
    )
    @request_data(
        keys=["stock_code", "serial_no", "component"]
    )
    def get_replacement_task_history(
        self,
        request,
        stock_code,
        serial_no,
        component
    ):
        '''
            desc: Get hard time replacement task history.

            json:
                stock_code: string
                serial_no: string
                component: string
        '''
        serializer = RepairOrderPartReplacementSerializer(
            PartsService.get_replacement_task_history(
                stock_code,
                serial_no,
                component
            ),
            many=True
        )

        return Response(
            status=status.HTTP_200_OK,
            data=serializer.data
        )


    @action(detail=False, methods=['post'], schema=AutoDocSchema(),
            url_name='check_status')
    @request_data(keys=['stock_code','serial_no'])
    @error_response()
    def check_status(self, request, stock_code, serial_no):
        '''
            desc: >
                Check status of a repair order

                ## Response

                ```

                {
                    "part_detail":{
                        "outstanding":0,
                        "message":[]
                    },
                    "version":{
                        "outstanding":0,
                        "message":[],
                    }
                    "edoc":{
                        "outstanding":0,
                        "message":[],
                    },
                    "hard_time_replacement":{
                        "outstanding":0,
                        "message":[],
                    }
                }

                ```

        '''
        return Response(
            status=status.HTTP_200_OK,
            data=PartsService.check_all_status(stock_code, serial_no)
        )

    @action(detail=False, methods=['get'], schema=AutoDocSchema(),
            url_name='get_parts_with_num_of_child')
    @error_response()
    def get_parts_with_num_of_child(self, request):
        '''
            desc: Get number of child parts.

        '''

        serializer=PartsWithChildSerializer(
            # PartsService.get_parts_with_num_of_child()
            Parts.objects.all().filter(deleted=False)
            # Parts.objects.all().filter(deleted=False).order_by("-id")[:100]
            ,
            many=True
        )

        return Response(
            status=status.HTTP_200_OK,
            data=serializer.data

        )

    @action(detail=False, methods=['post'], schema=AutoDocSchema(),
            url_name='get_parts_with_num_of_child_fillter')
    @request_data(keys=['stock_code', 'serial_no', 'description'])
    @error_response()
    def get_parts_with_num_of_child_fillter(self, request, stock_code, serial_no, description):
        '''
            desc: Get number of child parts.
        '''
        query = Q()
        if stock_code:
            query &= Q(stock_code__icontains=stock_code)
        if serial_no:
            query &= Q(serial_no__icontains=serial_no)
        if description:
            query &= Q(description__icontains=description)
        serializer=PartsWithChildSerializer(
            Parts.objects.all().filter(deleted=False).filter(query)
            ,
            many=True
        )
        return Response(
            status=status.HTTP_200_OK,
            data=serializer.data

        )

    @action(
        detail=False, methods=["DELETE"],
        url_name="soft_delete_part", schema=AutoDocSchema()
    )
    @request_data(
        keys=[
            "id"
        ]
    )
    @error_response()
    def soft_delete_part(
        self,
        request,
        id
    ):

        if PartsService.soft_delete_part(
            id,
            request.user
        ):
            response = {
                "code": 1,
                "message": "Success delete part"
            }

        return Response(status=status.HTTP_200_OK, data=response)

    @action(
        detail=False, methods=["POST"],
        url_name="get_qr_code_image", schema=AutoDocSchema()
    )
    @request_data(
        keys=[
            "part_id",
            "qrcode_size",
            "cews_size"
        ]
    )
    @error_response()
    def get_qr_code_image(
        self,
        request,
        part_id,
        qrcode_size,
        cews_size
    ):

        data = PartsService.get_qr_code_image(
            part_id,
            qrcode_size,
            cews_size
        )
        response = {
                "code": 1,
                "data": data,
            }

        return Response(status=status.HTTP_200_OK, data=response)

    @action(
        detail=False, methods=["POST"],
        url_name="get_childs_qr_code_image", schema=AutoDocSchema()
    )
    @request_data(
        keys=[
            "parent_part_id",
            "qrcode_size",
            "cews_size"
        ]
    )
    @error_response()
    def get_childs_qr_code_image(
        self,
        request,
        parent_part_id,
        qrcode_size,
        cews_size
    ):

        childs = Parts.objects.filter(parent_part__id=parent_part_id)

        data = []

        for child in childs:
            context = PartsService.get_qr_code_image(
                child.id,
                qrcode_size,
                cews_size
            )

            data.append(context)

        data.sort(key=lambda x: x.get('seq'))

        response = {
                "code": 1,
                "data": data,
            }

        return Response(status=status.HTTP_200_OK, data=response)
